package com.i360day.redis.aspect;

import com.alibaba.fastjson.JSON;
import com.i360day.redis.annotation.RepeatSubmit;
import com.i360day.redis.constant.RedisContextHolder;
import com.i360day.redis.exception.RepeatSubmitException;
import com.i360day.redis.lock.RedisLock;
import com.i360day.redis.util.IpUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @description: 重复提交处理
 * @author: liju.z
 * @create: 2019-11-13 10:16
 **/
@Aspect
public class RepeatSubmitAspect {

    private Logger log = LoggerFactory.getLogger(RepeatSubmitAspect.class);


    private String getTokenOnly() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String token = request.getHeader("token");
        if (ObjectUtils.isEmpty(token)) {
            return IpUtils.getIpAddr(request);
        }
        return token;
    }

    private String getCurrentRequestId(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String clazzName = joinPoint.getSignature().getDeclaringType().getName();
        Object[] params = joinPoint.getArgs();
        StringBuilder builder = new StringBuilder();
        if (params != null) {
            for (Object obj : params) {
                builder.append(JSON.toJSONString(obj));
            }
        }
        return RedisContextHolder.getRedisKey(clazzName, method.getName(), getTokenOnly(), builder.toString());
    }

    // 配置织入点
    @Pointcut("@annotation(com.i360day.redis.annotation.RepeatSubmit)")
    public void repeatSubmitAspect() {
    }


    @Before("repeatSubmitAspect()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 获得注解
        RepeatSubmit repeatSubmitAnnotation = getAnnotationLog(joinPoint);
        if (repeatSubmitAnnotation == null) return;
        //当前请求唯一标识
        String requestId = StringUtils.isEmpty(repeatSubmitAnnotation.key()) ? getCurrentRequestId(joinPoint) : repeatSubmitAnnotation.key();
        RedisLock redisLock = new RedisLock(requestId);
        redisLock.call(() -> {
            Integer redisValue = RedisContextHolder.get(requestId, Integer.class);
            //如果获取到的值，大于当前设置的次数则抛异常
            if (null != redisValue) {
                //重复提交
                if (redisValue > repeatSubmitAnnotation.limitCount()) {
                    throw new RepeatSubmitException(StringUtils.isEmpty(repeatSubmitAnnotation.errorMsg()) ? "请勿重复提交" : repeatSubmitAnnotation.errorMsg());
                }
                RedisContextHolder.set(requestId, redisValue + 1, repeatSubmitAnnotation.time());
            } else {
                RedisContextHolder.set(requestId, 1, repeatSubmitAnnotation.time());
            }
            return redisValue;
        });
    }

    @AfterReturning("repeatSubmitAspect()")
    public void afterReturning(JoinPoint joinPoint) {
        // 获得注解
        RepeatSubmit repeatSubmitAnnotation = getAnnotationLog(joinPoint);
        if (repeatSubmitAnnotation == null) return;
        //当前请求唯一标识
        if (repeatSubmitAnnotation.afterReturningDelete()) {
            String requestId = StringUtils.isEmpty(repeatSubmitAnnotation.key()) ? getCurrentRequestId(joinPoint) : repeatSubmitAnnotation.key();
            RedisContextHolder.delete(requestId);
        }
    }

    @AfterThrowing(pointcut = "repeatSubmitAspect()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        // 获得注解
        RepeatSubmit repeatSubmitAnnotation = getAnnotationLog(joinPoint);
        if (repeatSubmitAnnotation == null) return;
        else if (!repeatSubmitAnnotation.afterReturningDelete() || e instanceof RepeatSubmitException) return;
        //当前请求唯一标识
        String requestId = StringUtils.isEmpty(repeatSubmitAnnotation.key()) ? getCurrentRequestId(joinPoint) : repeatSubmitAnnotation.key();
        RedisContextHolder.delete(requestId);
    }

    /**
     * 是否存在注解，如果存在就获取
     *
     * @return
     */
    private RepeatSubmit getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(RepeatSubmit.class);
        }
        return null;
    }

}
